package top.atcra.rpc.protection.circuitBreaker.counter;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;

public class SlidingWindowErrorRateCounter implements ErrorRateCounter {
    private final Queue<WindowBucket> windowBuckets = new ConcurrentLinkedQueue<>();
    private final int windowDuration_ms;
    private final int bucketDuration_ms;
    // 整个窗口的请求数和错误数
    private final AtomicLong totalRequests = new AtomicLong(0);
    private final AtomicLong totalErrors = new AtomicLong(0);
    // 锁，用于同步对窗口桶的访问
    private final Object lock = new Object();

    /**
     *
     * @param windowDuration_ms 时间窗口大小，单位为毫秒
     * @param bucketDuration_ms 桶大小，单位为毫秒
     */
    public SlidingWindowErrorRateCounter(int windowDuration_ms,int bucketDuration_ms) {
        this.windowDuration_ms = windowDuration_ms;
        this.bucketDuration_ms = bucketDuration_ms;
    }

    public void recordRequest(boolean isError) {
        long currentTime = System.currentTimeMillis();
        cleanupBuckets(currentTime);

        synchronized (lock) {
            WindowBucket currentBucket = getCurrentOrCreateBucket(currentTime);
            if (isError) {
                currentBucket.errorCount.incrementAndGet();
                totalErrors.incrementAndGet();
            }
            currentBucket.requestCount.incrementAndGet();
            totalRequests.incrementAndGet();
        }
    }
    public void recordRequest(boolean success,long currentTime) {
        cleanupBuckets(currentTime);

        synchronized (lock) {
            WindowBucket currentBucket = getCurrentOrCreateBucket(currentTime);
            if (!success) {
                currentBucket.errorCount.incrementAndGet();
                totalErrors.incrementAndGet();
            }
            currentBucket.requestCount.incrementAndGet();
            totalRequests.incrementAndGet();
        }
    }

    public double getErrorRate() {
        long requests = totalRequests.get();
        long errors = totalErrors.get();
        return requests == 0 ? 0.0 : (double) errors / requests;
    }

    private void cleanupBuckets(long currentTime) {
        synchronized (lock) {
            while (!windowBuckets.isEmpty()) {
                WindowBucket oldestBucket = windowBuckets.peek();
                if (currentTime - oldestBucket.timestamp > windowDuration_ms) {
                    WindowBucket removed = windowBuckets.poll();
                    totalRequests.addAndGet(-removed.requestCount.get());
                    totalErrors.addAndGet(-removed.errorCount.get());
                } else {
                    break;
                }
            }
        }
    }

    private WindowBucket getCurrentOrCreateBucket(long currentTime) {
        WindowBucket firstBucket = windowBuckets.peek();
        if (firstBucket != null && currentTime-firstBucket.timestamp <bucketDuration_ms) {
            return firstBucket;
        }
        WindowBucket newBucket = new WindowBucket(currentTime);
        windowBuckets.offer(newBucket);
        return newBucket;
    }

    private static class WindowBucket {
        final long timestamp;
        final AtomicLong requestCount = new AtomicLong(0);
        final AtomicLong errorCount = new AtomicLong(0);

        WindowBucket(long timestamp) {
            this.timestamp = timestamp;
        }
    }
}